﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Specialized;
using System.Collections.Observable.Transaction;

namespace ObservableUnitTests
{
    [TestClass]
    public class PendingNotifyCollectionChangedEventTest
    {
        [TestMethod]
        public void MergeAdd_NoIndex()
        {
            //prepare data
            List<int> firstItems = new List<int> { 1, 2, 3};
            List<int> secondItems = new List<int> { 4, 5, 6};

            List<int> expectedList = new List<int>();
            expectedList.AddRange(firstItems);
            expectedList.AddRange(secondItems);

            //prepare event args
            NotifyCollectionChangedEventArgs firstEventArgs = 
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, firstItems);

            NotifyCollectionChangedEventArgs secondEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, secondItems);
            
            PendingCollectionChangedEvent pendingEvent = new PendingCollectionChangedEvent(firstEventArgs);

            Assert.IsTrue( pendingEvent.TryMerge(secondEventArgs) );

            NotifyCollectionChangedEventArgs mergedEvent = pendingEvent.EventArgs;

            //verify event argument members
            Assert.AreEqual(mergedEvent.Action, NotifyCollectionChangedAction.Add);
            Assert.AreEqual(-1, mergedEvent.NewStartingIndex);
            Assert.AreEqual(-1, mergedEvent.OldStartingIndex);

            Assert.IsNull(mergedEvent.OldItems);
            Assert.IsNotNull(mergedEvent.NewItems);

            Assert.AreEqual( expectedList.Count, mergedEvent.NewItems.Count );
            for (int i = 0; i < mergedEvent.NewItems.Count; i++)
            {
                Assert.AreEqual(expectedList[i], mergedEvent.NewItems[i]);
            }
        }

        [TestMethod]
        public void MergeAdd_Before()
        {
            //prepare data
            List<int> firstItems = new List<int> { 1, 2, 3 };
            List<int> secondItems = new List<int> { 4, 5, 6 };

            List<int> expectedList = new List<int>();            
            expectedList.AddRange(secondItems);
            expectedList.AddRange(firstItems);

            int addIndex = 10;
            
            
            NotifyCollectionChangedEventArgs firstEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, firstItems, addIndex);
            
            NotifyCollectionChangedEventArgs secondEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, secondItems, addIndex);
            
            
            PendingCollectionChangedEvent pendingEvent = new PendingCollectionChangedEvent(firstEventArgs);

            Assert.IsTrue(pendingEvent.TryMerge(secondEventArgs));

            NotifyCollectionChangedEventArgs mergedEvent = pendingEvent.EventArgs;

            //verify event argument members
            Assert.AreEqual(mergedEvent.Action, NotifyCollectionChangedAction.Add);

            Assert.AreEqual(addIndex, mergedEvent.NewStartingIndex);
            Assert.AreEqual(-1, mergedEvent.OldStartingIndex);

            Assert.IsNull(mergedEvent.OldItems);
            Assert.IsNotNull(mergedEvent.NewItems);

            Assert.AreEqual(expectedList.Count, mergedEvent.NewItems.Count);

            for (int i = 0; i < mergedEvent.NewItems.Count; i++)
            {
                Assert.AreEqual(expectedList[i], mergedEvent.NewItems[i]);
            }
        }

        [TestMethod]
        public void MergeAdd_After()
        {
            //prepare data
            List<int> firstItems = new List<int> { 1, 2, 3 };
            List<int> secondItems = new List<int> { 4, 5, 6 };

            List<int> expectedList = new List<int>();
            expectedList.AddRange(firstItems);
            expectedList.AddRange(secondItems);
            

            int firstAddIndex = 10;
            int secondAddIndex = firstAddIndex + firstItems.Count ;


            NotifyCollectionChangedEventArgs firstEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, firstItems, firstAddIndex);

            NotifyCollectionChangedEventArgs secondEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, secondItems, secondAddIndex);


            PendingCollectionChangedEvent pendingEvent = new PendingCollectionChangedEvent(firstEventArgs);

            Assert.IsTrue(pendingEvent.TryMerge(secondEventArgs));

            NotifyCollectionChangedEventArgs mergedEvent = pendingEvent.EventArgs;

            //verify event argument members
            Assert.AreEqual(mergedEvent.Action, NotifyCollectionChangedAction.Add);

            Assert.AreEqual(firstAddIndex, mergedEvent.NewStartingIndex);
            Assert.AreEqual(-1, mergedEvent.OldStartingIndex);

            Assert.IsNull(mergedEvent.OldItems);
            Assert.IsNotNull(mergedEvent.NewItems);

            Assert.AreEqual(expectedList.Count, mergedEvent.NewItems.Count);

            for (int i = 0; i < mergedEvent.NewItems.Count; i++)
            {
                Assert.AreEqual(expectedList[i], mergedEvent.NewItems[i]);
            }
        }

        [TestMethod]
        public void MergeAdd_NonMergebleByIndex()
        {
            //prepare data
            List<int> basicItems = new List<int> { 1, 2, 3 };
            List<int> helperItems = new List<int> { 4, 5, 6 };


            int basicIndex = 10;
            int beforeIndex = basicIndex - 1;
            int insideIndex = basicIndex + (basicItems.Count - 1);
            int afterIndex = basicIndex + basicItems.Count + 1;

            NotifyCollectionChangedEventArgs basicEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, basicItems, basicIndex);

            NotifyCollectionChangedEventArgs beforeEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, basicItems, beforeIndex);

            NotifyCollectionChangedEventArgs insideEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, basicItems, insideIndex);

            NotifyCollectionChangedEventArgs afterEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, basicItems, afterIndex);


            PendingCollectionChangedEvent pendingEvent = new PendingCollectionChangedEvent(basicEventArgs);

            Assert.IsFalse(pendingEvent.TryMerge(beforeEventArgs));
            Assert.IsFalse(pendingEvent.TryMerge(insideEventArgs));
            Assert.IsFalse(pendingEvent.TryMerge(afterEventArgs));
        }

        [TestMethod]
        public void MergeRemove_NoIndex()
        {
            //prepare data
            List<int> firstItems = new List<int> { 1, 2, 3 };
            List<int> secondItems = new List<int> { 4, 5, 6 };

            List<int> expectedList = new List<int>();
            expectedList.AddRange(firstItems);
            expectedList.AddRange(secondItems);

            //prepare event args
            NotifyCollectionChangedEventArgs firstEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, firstItems);

            NotifyCollectionChangedEventArgs secondEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, secondItems);



            PendingCollectionChangedEvent pendingEvent = new PendingCollectionChangedEvent(firstEventArgs);

            Assert.IsTrue(pendingEvent.TryMerge(secondEventArgs));

            NotifyCollectionChangedEventArgs mergedEvent = pendingEvent.EventArgs;

            //verify event argument members
            Assert.AreEqual(mergedEvent.Action, NotifyCollectionChangedAction.Remove);

            Assert.AreEqual(-1, mergedEvent.NewStartingIndex);
            Assert.AreEqual(-1, mergedEvent.OldStartingIndex);            

            Assert.IsNull(mergedEvent.NewItems);
            Assert.IsNotNull(mergedEvent.OldItems);            

            Assert.AreEqual(expectedList.Count, mergedEvent.OldItems.Count);
            for (int i = 0; i < mergedEvent.OldItems.Count; i++)
            {
                Assert.AreEqual(expectedList[i], mergedEvent.OldItems[i]);
            }
        }

        [TestMethod]
        public void MergeRemove_Before()
        {
            //prepare data
            List<int> firstItems = new List<int> { 1, 2, 3 };
            List<int> secondItems = new List<int> { 4, 5, 6 };

            List<int> expectedList = new List<int>();
            expectedList.AddRange(secondItems);
            expectedList.AddRange(firstItems);

            int firstRemoveIndex = 10;
            int secondRemoveIndex = firstRemoveIndex - secondItems.Count;


            NotifyCollectionChangedEventArgs firstEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, firstItems, firstRemoveIndex);

            NotifyCollectionChangedEventArgs secondEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, secondItems, secondRemoveIndex);


            PendingCollectionChangedEvent pendingEvent = new PendingCollectionChangedEvent(firstEventArgs);

            Assert.IsTrue(pendingEvent.TryMerge(secondEventArgs));

            NotifyCollectionChangedEventArgs mergedEvent = pendingEvent.EventArgs;

            //verify event argument members
            Assert.AreEqual(mergedEvent.Action, NotifyCollectionChangedAction.Remove);

            Assert.AreEqual(secondRemoveIndex, mergedEvent.OldStartingIndex);
            Assert.AreEqual(-1, mergedEvent.NewStartingIndex);

            Assert.IsNull(mergedEvent.NewItems);
            Assert.IsNotNull(mergedEvent.OldItems);

            Assert.AreEqual(expectedList.Count, mergedEvent.OldItems.Count);

            for (int i = 0; i < mergedEvent.OldItems.Count; i++)
            {
                Assert.AreEqual(expectedList[i], mergedEvent.OldItems[i]);
            }
        }

        [TestMethod]
        public void MergeRemove_After()
        {
            //prepare data
            List<int> firstItems = new List<int> { 1, 2, 3 };
            List<int> secondItems = new List<int> { 4, 5, 6 };

            List<int> expectedList = new List<int>();
            expectedList.AddRange(firstItems);
            expectedList.AddRange(secondItems);


            int removeIndex = 10;
            

            NotifyCollectionChangedEventArgs firstEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, firstItems, removeIndex);

            NotifyCollectionChangedEventArgs secondEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, secondItems, removeIndex);


            PendingCollectionChangedEvent pendingEvent = new PendingCollectionChangedEvent(firstEventArgs);

            Assert.IsTrue(pendingEvent.TryMerge(secondEventArgs));

            NotifyCollectionChangedEventArgs mergedEvent = pendingEvent.EventArgs;

            //verify event argument members
            Assert.AreEqual(mergedEvent.Action, NotifyCollectionChangedAction.Remove);

            Assert.AreEqual(removeIndex, mergedEvent.OldStartingIndex);
            Assert.AreEqual(-1, mergedEvent.NewStartingIndex);
            
            Assert.IsNull(mergedEvent.NewItems);
            Assert.IsNotNull(mergedEvent.OldItems);

            Assert.AreEqual(expectedList.Count, mergedEvent.OldItems.Count);

            for (int i = 0; i < mergedEvent.OldItems.Count; i++)
            {
                Assert.AreEqual(expectedList[i], mergedEvent.OldItems[i]);
            }
        }

        [TestMethod]
        public void MergeRemove_NonMergebleByIndex()
        {
            //prepare data
            List<int> basicItems = new List<int> { 1, 2, 3 };
            List<int> helperItems = new List<int> { 4, 5, 6 };


            int basicIndex = 10;
            int beforeIndex = basicIndex - helperItems.Count - 1;
            int insideIndex = basicIndex - helperItems.Count + 1;
            int afterIndex = basicIndex + 1;

            NotifyCollectionChangedEventArgs basicEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, basicItems, basicIndex);

            NotifyCollectionChangedEventArgs beforeEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, basicItems, beforeIndex);

            NotifyCollectionChangedEventArgs insideEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, basicItems, insideIndex);

            NotifyCollectionChangedEventArgs afterEventArgs =
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, basicItems, afterIndex);


            PendingCollectionChangedEvent pendingEvent = new PendingCollectionChangedEvent(basicEventArgs);

            Assert.IsFalse(pendingEvent.TryMerge(beforeEventArgs));
            Assert.IsFalse(pendingEvent.TryMerge(insideEventArgs));
            Assert.IsFalse(pendingEvent.TryMerge(afterEventArgs));
        }
    }
}
